package com.xc.disk.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xc.disk.bean.FileBean;
import com.xc.disk.bean.FolderFileBean;
import com.xc.disk.bean.NoticeBean;
import com.xc.disk.bean.SystemFolderBean;
import com.xc.disk.config.Constants;
import com.xc.disk.dto.FileDto;
import com.xc.disk.dto.UploadDto;
import com.xc.disk.dto.UserDto;
import com.xc.disk.entity.FileEntity;
import com.xc.disk.entity.FolderEntity;
import com.xc.disk.entity.SafetyChainEntity;
import com.xc.disk.enums.RedisLock;
import com.xc.disk.exception.OperateException;
import com.xc.disk.info.HashInfo;
import com.xc.disk.info.SourceInfo;
import com.xc.disk.info.TokenInfo;
import com.xc.disk.mapper.FileMapper;
import com.xc.disk.mapper.FolderMapper;
import com.xc.disk.mapper.SafetyChainMapper;
import com.xc.disk.service.FileService;
import com.xc.disk.service.FolderService;
import com.xc.disk.service.UserService;
import com.xc.disk.utils.RedisUtils;
import com.xc.tool.utils.Md5Utils;
import com.xc.tool.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * <p>项目名称: Stars</p>
 * <p>描述: 文件管理实现</p>
 * <P>创建人:小程 </p>
 * <P>创建人邮箱: 779943476@qq.com</p>
 * <p>创建时间: 2019/9/27</p>
 *
 * @version v1.0
 */
@Service
@Slf4j
public class FileServiceImpl extends ServiceImpl<FileMapper, FileEntity> implements FileService {

    @Autowired
    private FolderService folderService;
    @Autowired
    private FolderMapper folderMapper;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private Constants constants;
    @Autowired
    private UserService userService;
    @Autowired
    private SafetyChainMapper safetyChainMapper;

    @Override
    @Transactional
    public void moveFileFolder(TokenInfo token, List<FolderFileBean> folderFiles) {
        String parentId = null;
        String newParentId = null;
        long size = 0;
        for (FolderFileBean folderFileBean : folderFiles) {
            newParentId = folderFileBean.getParentId();
            if (folderFileBean.getIsFile()) {
                FileEntity fileEntity = fileMapper.selectById(folderFileBean.getId());
                if (!fileEntity.getUserId().equals(token.getUserId())) {
                    throw new OperateException("f027", "你的账号没有操作该数据的权限！");
                }
                FileEntity entity = new FileEntity();
                entity.setFolderId(folderFileBean.getParentId());
                entity.setUserId(token.getUserId());
                entity.setName(fileEntity.getName());
                entity = fileMapper.selectOne(new QueryWrapper<>(entity));
                if (entity != null) {
                    throw new OperateException("", "移动失败，文件已经存在！");
                }
                //移动文件
                size += fileEntity.getSize();
                parentId = fileEntity.getFolderId();
                fileEntity.setCreateTime(null);
                fileEntity.setFolderId(folderFileBean.getParentId());
                if (!this.updateById(fileEntity)) {
                    throw new OperateException("f031", "修改文件失败！");
                }
            } else {
                if (folderService.verifyFolder(folderFileBean.getParentId(), folderFileBean.getId())) {
                    throw new OperateException("f041", "操作错误,无法向下级移动！");
                }
                //移动目录
                FolderEntity folderEntity = folderMapper.selectById(folderFileBean.getId());
                if (!folderEntity.getUserId().equals(token.getUserId())) {
                    throw new OperateException("f027", "你的账号没有操作该数据的权限！");
                }
                FolderEntity entity = new FolderEntity();
                entity.setUserId(token.getUserId());
                entity.setParentId(folderFileBean.getParentId());
                entity.setName(folderEntity.getName());
                entity = folderMapper.selectOne(new QueryWrapper<>(entity));
                if (entity != null) {
                    throw new OperateException("f027", "移动失败，目录已存在！");
                }
                parentId = folderEntity.getParentId();
                size += folderEntity.getSize();
                folderEntity.setParentId(folderFileBean.getParentId());
                folderEntity.setUpdateTime(null);
                //修改目录
                if (!retBool(folderMapper.updateById(folderEntity))) {
                    throw new OperateException("f040", "修改目录失败！");
                }
            }

        }
        //计算大小
        long finalSize = size;
        folderService.seekFolderCrossPoint(parentId, newParentId, null, (crossPointInfo) -> {
            //计算上级目录大小
            for (FolderEntity folderEntity : crossPointInfo.getFolders()) {
                folderEntity.setSize(folderEntity.getSize() - finalSize);
            }
            for (FolderEntity folderEntity : crossPointInfo.getNewFolders()) {
                folderEntity.setSize(folderEntity.getSize() + finalSize);
            }
            List<FolderEntity> folderList = new ArrayList<>();
            folderList.addAll(crossPointInfo.getNewFolders());
            folderList.addAll(crossPointInfo.getFolders());
            if (!folderService.updateBatchById(folderList)) {
                throw new OperateException("c002", "修改失败！");
            }
        });
    }

    @Override
    @Transactional
    public void copyFileFolder(TokenInfo token, List<FolderFileBean> folderFiles) {
        long size = 0;
        String parentId = null;
        for (FolderFileBean folderFileBean : folderFiles) {
            parentId = folderFileBean.getParentId();
            if (folderFileBean.getIsFile()) {
                //复制文件
                FileEntity fileEntity = fileMapper.selectById(folderFileBean.getId());
                if (!fileEntity.getUserId().equals(token.getUserId())) {
                    throw new OperateException("f027", "你的账号没有操作该数据的权限！");
                }
                size += fileEntity.getSize();
                // 去重
                FileEntity entity = new FileEntity();
                entity.setFolderId(folderFileBean.getParentId());
                entity.setUserId(token.getUserId());
                entity.setName(fileEntity.getName());
                entity = fileMapper.selectOne(new QueryWrapper<>(entity));
                if (entity != null) {
                    String name = rename(fileEntity.getName(), folderFileBean.getParentId(), token.getUserId());
                    fileEntity.setName(name);
                }
                fileEntity.setId(null);
                fileEntity.setCreateTime(null);
                fileEntity.setUpdateTime(null);
                fileEntity.setVersion(null);
                fileEntity.setFolderId(folderFileBean.getParentId());
                if (!this.save(fileEntity)) {
                    throw new OperateException("f030", "创建文件失败！");
                }
            } else {
                if (folderService.verifyFolder(folderFileBean.getParentId(), folderFileBean.getId())) {
                    throw new OperateException("f042", "操作错误,无法向下级复制！");
                }
                //复制目录
                FolderEntity folderEntity = folderMapper.selectById(folderFileBean.getId());
                if (!folderEntity.getUserId().equals(token.getUserId())) {
                    throw new OperateException("f027", "你的账号没有操作该数据的权限！");
                }
                size += folderEntity.getSize();
                FolderEntity entity = new FolderEntity();
                entity.setUserId(token.getUserId());
                entity.setName(folderEntity.getName());
                entity.setParentId(folderFileBean.getParentId());
                entity = folderMapper.selectOne(new QueryWrapper<>(entity));
                if (entity != null) {
                    String name = rename(folderEntity.getName(), folderFileBean.getParentId(), token.getUserId());
                    folderEntity.setName(name);
                }
                folderEntity.setId(null);
                folderEntity.setUpdateTime(null);
                folderEntity.setVersion(null);
                folderEntity.setCreateTime(null);
                folderEntity.setParentId(folderFileBean.getParentId());
                if (!this.retBool(folderMapper.insert(folderEntity))) {
                    throw new OperateException("f028", "创建目录失败！");
                }
                //复制文件及以下目录
                folderService.subdirectoryHandle(
                        folderFileBean.getId(),
                        folderEntity.getId(),
                        (subdirectoryInfo) -> {
                            if (subdirectoryInfo.getIsFile()) {
                                List<FileEntity> files = subdirectoryInfo.getFiles();
                                for (FileEntity fileEntity : files) {
                                    if (!fileEntity.getUserId().equals(token.getUserId())) {
                                        throw new OperateException("f027", "你的账号没有操作该数据的权限！");
                                    }
                                    //复制文件
                                    fileEntity.setId(null);
                                    fileEntity.setUpdateTime(null);
                                    fileEntity.setCreateTime(null);
                                    fileEntity.setVersion(null);
                                    fileEntity.setFolderId(subdirectoryInfo.getNewParentId());
                                    if (!this.save(fileEntity)) {
                                        throw new OperateException("f030", "创建文件失败！");
                                    }
                                }
                            } else {
                                FolderEntity folder = subdirectoryInfo.getFolder();
                                if (!folder.getUserId().equals(token.getUserId())) {
                                    throw new OperateException("f027", "你的账号没有操作该数据的权限！");
                                }
                                //复制目录
                                folder.setId(null);
                                folder.setUpdateTime(null);
                                folder.setCreateTime(null);
                                folder.setVersion(null);
                                folder.setParentId(subdirectoryInfo.getNewParentId());
                                if (!this.retBool(folderMapper.insert(folder))) {
                                    throw new OperateException("f028", "创建目录失败！");
                                }
                            }
                        });
            }

        }
        //加法计算文件大小
        folderService.addFolderSize(parentId, size);
    }

    @Override
    public String rename(String name, String parentId, String userId) {
        FolderEntity folder = new FolderEntity();
        folder.setParentId(parentId);
        folder.setUserId(userId);
        List<FolderEntity> fileEntities = folderMapper.selectList(new QueryWrapper<>(folder));
        int i = 0;
        while (true) {
            i++;
            boolean bool = true;
            String suffix = "";
            String fileName = name;
            //删除后缀
            if (name.contains(".")) {
                int j = name.lastIndexOf(".");
                fileName = name.substring(0, j);
                suffix = name.substring(j, name.length());
            }
            for (FolderEntity folderEntity : fileEntities) {
                if (folderEntity.getName().equals(fileName + "_" + i + suffix)) {
                    bool = false;
                }
            }
            if (bool) {
                return fileName + "_" + i + StringUtils.random(4) + suffix;
            }
        }
    }

    @Override
    public String getDownloadUrl(FileEntity fileEntity) {
        SourceInfo sourceInfo = getSource(fileEntity.getHashCode());
        if (sourceInfo == null) {
            throw new OperateException("f046", "未找到文件下载地址！");
        }
        String suffix = fileEntity.getName().substring(fileEntity.getName().lastIndexOf("."), fileEntity.getName().length());
        String fid = Md5Utils.getSaltMd5(UUID.randomUUID().toString());
        RedisUtils.set(constants.getDownloadKey() + fid, fileEntity, 1800000l);
        return sourceInfo.getLocalUrl() + constants.getDownloadUrl() + fileEntity.getHashCode() + suffix + "?fid=" + fid + "&time=" + new Date().getTime();
    }

    @Override
    public FileEntity getFileById(String id) {
        return fileMapper.selectById(id);
    }

    @Override
    public List<FileEntity> getFileByFolderId(String folderId) {
        FileEntity fileEntity = new FileEntity();
        fileEntity.setFolderId(folderId);
        return fileMapper.selectList(new QueryWrapper<>(fileEntity));
    }

    @Override
    public FileEntity updateFile(TokenInfo<UserDto> token, String id, String name) {
        FileEntity fileEntity = getById(id);
        if (!token.getUserId().equals(fileEntity.getUserId())) {
            throw new OperateException("f027", "你的账号没有操作该数据的权限！");
        }
        fileEntity.setName(name);
        if (!this.updateById(fileEntity)) {
            throw new OperateException("f031", "修改文件失败！");
        }
        return fileEntity;
    }

    @Override
    public FileEntity getDevFile(HttpServletRequest request, String fid) {
        String url = request.getHeader("Referer");
        if (url == null) {
            url = "";
        }
        FileEntity fileEntity = fileMapper.selectById(fid);
        if (fileEntity == null) {
            throw new OperateException("f032", "文件不存在！");
        }
        SafetyChainEntity safetyChainEntity = new SafetyChainEntity();
        safetyChainEntity.setUserId(fileEntity.getUserId());
        List<SafetyChainEntity> safetyChains = safetyChainMapper.selectList(new QueryWrapper<>(safetyChainEntity));
        if (safetyChains == null || safetyChains.size() == 0) {
            throw new OperateException("f032", "你没有配置允许访问地址！");
        }

        for (SafetyChainEntity safetyChain : safetyChains) {
            if (verifyUrl(url, safetyChain.getUrl())) {
                FolderEntity folderEntity = folderService.getRootFolders(fileEntity.getFolderId());
                if (folderEntity != null && folderEntity.getName().equals(constants.getDevFolder())) {
                    try {
                        if (!RedisUtils.lock(RedisLock.FILE_LOCK.getKey(), System.currentTimeMillis())) {
                            throw new OperateException("f054", "文件操作超时！");
                        }
                        userService.computeFreeFlow(fileEntity.getUserId(), fileEntity.getSize(), false);
                        return fileEntity;
                    } finally {
                        RedisUtils.unlock(RedisLock.FILE_LOCK.getKey());
                    }
                }
            }
        }
        throw new OperateException("f032", "你没有权限访问该地址！");
    }

    @Override
    @Transactional
    public void deleteDevFile(List<String> ids, String userId) {
        Map<String, FileEntity> hashCodeMap = new Hashtable<>();
        List<FileEntity> entityList = fileMapper.selectBatchIds(ids);
        long size = 0;
        for (FileEntity fileEntity : entityList) {
            if (!fileEntity.getUserId().equals(userId)) {
                throw new OperateException("", "传入的文件id不正确！");
            }
            size += fileEntity.getSize();
            hashCodeMap.put(fileEntity.getHashCode(), fileEntity);
        }
        if (!this.retBool(fileMapper.deleteBatchIds(ids))) {
            throw new OperateException("", "删除文件信息失败！");
        }
        //计算使用空间
        userService.computeUseSpace(userId, size, false);
        folderService.deleteSourceInfo(new ArrayList<>(hashCodeMap.keySet()));
    }

    @Override
    @Transactional
    public FileEntity createFileInfo(FileDto fileDto) {
        //查询文件信息
        FileEntity fileEntity = new FileEntity();
        fileEntity.setUserId(fileDto.getUserId());
        fileEntity.setSize(fileDto.getSize());
        fileEntity.setName(fileDto.getName());
        fileEntity.setFolderId(fileDto.getFolderId());
        fileEntity.setHashCode(fileDto.getHashCode());
        FileEntity entity = fileMapper.selectOne(new QueryWrapper<>(fileEntity));
        //判断文件是否添加
        if (entity != null) {
            //文件已经创建，重命名创建文件
            String name = rename(entity.getName(), entity.getFolderId(), fileDto.getUserId());
            fileEntity.setName(name);
            fileEntity.setFolderId(entity.getFolderId());
            if (!this.retBool(fileMapper.insert(fileEntity))) {
                throw new OperateException("f030", "创建文件失败！");
            }
        } else {
            //没有创建，创建文件
            String folderId = folderService.createFolder(fileDto.getFolders(), 0, fileDto.getUserId(), fileDto.getFolderId());
            fileEntity.setFolderId(folderId);
            FileEntity entity1 = fileMapper.selectOne(new QueryWrapper<>(fileEntity));
            if (entity1 == null) {
                if (!this.retBool(fileMapper.insert(fileEntity))) {
                    throw new OperateException("f030", "创建文件失败！");
                }
            } else {
                //文件已经创建，重命名创建文件
                String name = rename(entity1.getName(), entity1.getFolderId(), fileDto.getUserId());
                fileEntity.setName(name);
                fileEntity.setFolderId(folderId);
                if (!this.retBool(fileMapper.insert(fileEntity))) {
                    throw new OperateException("f030", "创建文件失败！");
                }
            }
        }
        //计算使用空间
        userService.computeUseSpace(fileEntity.getUserId(), fileEntity.getSize(), true);
        // 计算上级目录空间
        folderService.addFolderSize(fileEntity.getFolderId(), fileEntity.getSize());
        //返回结果
        return fileEntity;
    }


    /**
     * 验证url地址
     *
     * @param url       传入地址
     * @param verifyUrl 匹配地址
     * @return 返回是否成功true:成功
     */
    private boolean verifyUrl(String url, String verifyUrl) {
        String[] urls = url.split("/");
        String[] verifyUrls = verifyUrl.split("/");
        boolean bool = true;
        for (int i = 0; i < urls.length; i++) {
            if (i >= verifyUrls.length) {
                return false;
            }
            if (verifyUrls[i].contains("**")) {
                break;
            } else if (verifyUrls[i].contains("*")) {
                continue;
            } else {
                if (!urls[i].contains(verifyUrls[i])) {
                    bool = false;
                }
            }
        }
        return bool;
    }


    @Override
    public SourceInfo getSource(String hashCode) {
        // 检索本系统的服务地址
        List<NoticeBean> noticeBeans = RedisUtils.get(constants.getFileServerKey());
        if (noticeBeans == null || noticeBeans.size() == 0) {
            return null;
        }
        for (NoticeBean noticeBean : noticeBeans) {
            if (noticeBean.getHashInfos() == null || noticeBean.getHashInfos().size() == 0) {
                return null;
            }
            for (HashInfo hashInfo : noticeBean.getHashInfos()) {
                if (hashInfo.getHashCode().equals(hashCode)) {
                    SourceInfo sourceInfo = new SourceInfo();
                    sourceInfo.setAppId(noticeBean.getAppId());
                    sourceInfo.setLocalUrl(noticeBean.getLocalUrl());
                    sourceInfo.setHashCode(hashInfo.getHashCode());
                    sourceInfo.setSize(hashInfo.getSize());
                    return sourceInfo;
                }
            }
        }
        return null;
    }


    @Override
    public SourceInfo getSourceInfo(Long size) {
        List<NoticeBean> noticeBeans = RedisUtils.get(constants.getFileServerKey());
        if (noticeBeans == null || noticeBeans.size() == 0) {
            return null;
        }
        for (NoticeBean noticeBean : noticeBeans) {
            for (SystemFolderBean systemFolderBean : noticeBean.getFolderBeans()) {
                if (systemFolderBean.getAvailableSpace() > size) {
                    SourceInfo sourceInfo = new SourceInfo();
                    sourceInfo.setLocalUrl(noticeBean.getLocalUrl());
                    sourceInfo.setAppId(noticeBean.getAppId());
                    return sourceInfo;
                }
            }
        }
        return null;
    }
}
